﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.Common;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
using System.IO;
using System.Diagnostics;
using GenericLibrary.Xml;

namespace GenericLibrary.Xml.Xsl {

    public abstract class XsltExtensionBase : MarshalByRefObject {

        public virtual string NamespaceUri {
            get {
                return string.Format( "{0}.{1}", this.GetType().Namespace, this.GetType().Name );
            }
        }
    }

    public class XsltExtension : XsltExtensionBase {

        public XsltExtension() { }

        public XPathNavigator Document( string prefix, string localName, string namespaceUri ) {
            try {
                XmlDocument doc = new XmlDocument();
                return doc.AppendChild( doc.CreateElement( prefix, localName, namespaceUri ) ).CreateNavigator();
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XPathNavigator Document( string xml ) {
            try {
                XmlDocument doc = new XmlDocument();
                doc.LoadXml( xml );
                return doc.CreateNavigator().SelectSingleNode( "child::node()[1]" );
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XPathNavigator ReadOnlyDocument( string xml ) {
            try {
                return new XPathDocument( new StringReader( xml ) ).CreateNavigator().SelectSingleNode( "child::node()[1]" );
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XPathNavigator AppendChildElement( XPathNavigator contextNode, string prefix, string localName, string namespaceUri, string value ) {
            try {
                contextNode.AppendChildElement( prefix, localName, namespaceUri, value );
                return contextNode.SelectSingleNode( "child::node()[last()]" );
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XPathNavigator AppendChild( XPathNavigator contextNode, XPathNavigator newChild ) {
            try {
                contextNode.AppendChild( newChild );
                return contextNode.SelectSingleNode( "child::node()[last()]" );
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XPathNavigator PrependChildElement( XPathNavigator contextNode, string prefix, string localName, string namespaceUri, string value ) {
            try {
                contextNode.PrependChildElement( prefix, localName, namespaceUri, value );
                return contextNode.SelectSingleNode( "child::node()[1]" );
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XPathNavigator PrependChild( XPathNavigator contextNode, XPathNavigator newChild ) {
            try {
                contextNode.PrependChild( newChild );
                return contextNode.SelectSingleNode( "child::node()[1]" );
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XPathNavigator InsertElementAfter( XPathNavigator contextNode, string prefix, string localName, string namespaceUri, string value ) {
            try {
                contextNode.InsertElementAfter( prefix, localName, namespaceUri, value );
                return contextNode.SelectSingleNode( "following-sibling::node()[1]" );
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XPathNavigator InsertAfter( XPathNavigator contextNode, XPathNavigator newSibling ) {
            try {
                contextNode.InsertAfter( newSibling );
                return contextNode.SelectSingleNode( "following-sibling::node()[1]" );
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XPathNavigator InsertElementBefore( XPathNavigator contextNode, string prefix, string localName, string namespaceUri, string value ) {
            try {
                contextNode.InsertElementBefore( prefix, localName, namespaceUri, value );
                return contextNode.SelectSingleNode( "preceding-sibling::node()[1]" );
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XPathNavigator InsertBefore( XPathNavigator contextNode, XPathNavigator newSibling ) {
            try {
                contextNode.InsertBefore( newSibling );
                return contextNode.SelectSingleNode( "preceding-sibling::node()[1]" );
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XPathNavigator CreateAttribute( XPathNavigator contextNode, string prefix, string localName, string namespaceUri, string value ) {
            try {
                contextNode.CreateAttribute( prefix, localName, namespaceUri, value );
                if( namespaceUri != null && namespaceUri.Length > 0 ) {
                    return contextNode.SelectSingleNode( string.Format( "attribute::*[local-name()='{0}' and namespace-uri()='{1}']", localName, namespaceUri ) );
                } else {
                    return contextNode.SelectSingleNode( string.Format( "attribute::*[local-name()='{0}']", localName ) );
                }
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XPathNavigator SetValue( XPathNavigator contextNode, string value ) {
            try {
                contextNode.SetValue( value );
                return contextNode;
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XPathNavigator DeleteSelf( XPathNavigator node ) {
            try {
                node.DeleteSelf();
                return node;
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        //public XPathNavigator IndexNodes( XPathNodeIterator nodeSet, string keyExpression ) {
        //    try {
        //        XPathExpression keyExpr = XPathExpression.Compile( keyExpression );
        //        Dictionary<object, XPathNavigator> nodeIndexer = new Dictionary<object, XPathNavigator>();
        //        foreach( XPathNavigator node in nodeSet ) {
        //            nodeIndexer.Add( node.Evaluate( keyExpr ), node );
        //        }
        //        return new ClrObjectWrapper( nodeIndexer );
        //    } catch( Exception ) {
        //        Debug.Assert( false );
        //        throw;
        //    }
        //}

        //public XPathNavigator NodeByIndex( XPathNavigator clrNodeIndexer, object index ) {
        //    try {
        //        return ( clrNodeIndexer.UnderlyingObject as Dictionary<object, XPathNavigator> )[ index ];
        //    } catch( Exception ) {
        //        Debug.Assert( false );
        //        throw;
        //    }
        //}

    }

    //public class XsltDbExtension : XsltExtensionBase {

    //    private DbProviderFactory _dbProvider;
    //    private string _dbConnectionString;

    //    public XsltDbExtension( DbProviderFactory dbProvider, string dbConnectionString )
    //        : base() {
    //        this._dbProvider = dbProvider;
    //        this._dbConnectionString = dbConnectionString;
    //    }

    //    public XsltDbExtension( string dbProviderName, string dbConnectionString )
    //        : base() {
    //        try {
    //            this._dbProvider = DbProviderFactories.GetFactory( dbProviderName );
    //            this._dbConnectionString = dbConnectionString;
    //        } catch( Exception ) {
    //            Debug.Assert( false );
    //            throw;
    //        }
    //    }

    //    private bool ValidateSql( string sql ) {
    //        try {
    //            string _sql = sql.ToLower();
    //            return _sql.Contains( "insert" ) == false && _sql.Contains( "delete" ) == false && _sql.Contains( "update" ) == false;
    //        } catch( Exception ) {
    //            Debug.Assert( false );
    //            throw;
    //        }
    //    }

    //    public object DbScalar( string sql ) {
    //        try {
    //            if( !this.ValidateSql( sql ) ) {
    //                throw new Exception();
    //            }
    //            using( DbConnection connection = this._dbProvider.CreateConnection() ) {
    //                connection.ConnectionString = this._dbConnectionString;
    //                DbCommand command = this._dbProvider.CreateCommand();
    //                command.Connection = connection;
    //                command.CommandText = sql;
    //                connection.Open();
    //                return command.ExecuteScalar();
    //            }
    //        } catch( Exception ) {
    //            Debug.Assert( false );
    //            throw;
    //        }
    //    }

    //    public XPathNavigator ReadOnlyDbDocument( string sql ) {
    //        try {
    //            if( !this.ValidateSql( sql ) ) {
    //                throw new Exception();
    //            }
    //            using( DbConnection connection = this._dbProvider.CreateConnection() ) {
    //                connection.ConnectionString = this._dbConnectionString;
    //                DbCommand command = this._dbProvider.CreateCommand();
    //                command.Connection = connection;
    //                command.CommandText = sql;
    //                connection.Open();
    //                return new XPathDocument( new XmlDbDataReader( command.ExecuteReader( CommandBehavior.KeyInfo ) ) ).CreateNavigator().SelectSingleNode( "child::node()[1]" );
    //            }
    //        } catch( Exception ) {
    //            Debug.Assert( false );
    //            throw;
    //        }
    //    }

    //    public XPathNavigator DbDocument( string sql ) {
    //        try {
    //            if( !this.ValidateSql( sql ) ) {
    //                throw new Exception();
    //            }
    //            using( DbConnection connection = this._dbProvider.CreateConnection() ) {
    //                connection.ConnectionString = this._dbConnectionString;
    //                DbCommand command = this._dbProvider.CreateCommand();
    //                command.Connection = connection;
    //                command.CommandText = sql;
    //                connection.Open();
    //                XmlDocument doc = new XmlDocument();
    //                doc.Load( new XmlDbDataReader( command.ExecuteReader( CommandBehavior.KeyInfo ) ) );
    //                return doc.CreateNavigator().SelectSingleNode( "child::node()[1]" );
    //            }
    //        } catch( Exception ) {
    //            Debug.Assert( false );
    //            throw;
    //        }
    //    }
    //}
}
